package com.elitesland.b2c.tms.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.elitesland.b2c.com.service.ComAreaService;
import com.elitesland.b2c.tms.convert.TmsFareTmplAreaConvert;
import com.elitesland.b2c.tms.convert.TmsFareTmplConvert;
import com.elitesland.b2c.tms.dto.TmsFareTmplDTO;
import com.elitesland.b2c.tms.entity.QTmsFareTmplDO;
import com.elitesland.b2c.tms.entity.TmsFareTmplAreaDO;
import com.elitesland.b2c.tms.entity.TmsFareTmplDO;
import com.elitesland.b2c.tms.repo.TmsFareTmplAreaRepo;
import com.elitesland.b2c.tms.repo.TmsFareTmplAreaRepoProc;
import com.elitesland.b2c.tms.repo.TmsFareTmplRepo;
import com.elitesland.b2c.tms.repo.TmsFareTmplRepoProc;
import com.elitesland.b2c.tms.service.TmsFareTmplService;
import com.elitesland.b2c.tms.vo.com.TmsFareAreaComVO;
import com.elitesland.b2c.tms.vo.param.TmsFareTmplPageParamVO;
import com.elitesland.b2c.tms.vo.resp.TmsFareTmplDetailRespVO;
import com.elitesland.b2c.tms.vo.resp.TmsFareTmplPageRespVO;
import com.elitesland.b2c.tms.vo.save.TmsFareTmplSaveVO;
import com.elitesland.core.base.ApiResult;
import com.elitesland.core.base.PagingVO;
import com.elitesland.core.constant.CommonConstant;
import com.elitesland.core.constant.UdcEnum;
import com.elitesland.system.entity.SysUdcDO;
import com.elitesland.system.service.SysUdcService;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 功能说明
 * </p>
 *
 * @author fred
 * @since 2021-02-03 19:16:06
 */
@Service
public class TmsFareTmplServiceImpl implements TmsFareTmplService {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private TmsFareTmplRepo tmsFareTmplRepo;
    @Autowired
    private TmsFareTmplRepoProc tmsFareTmplRepoProc;
    @Autowired
    private TmsFareTmplAreaRepo tmsFareTmplAreaRepo;
    @Autowired
    private TmsFareTmplAreaRepoProc tmsFareTmplAreaRepoProc;
    @Autowired
    private ComAreaService comAreaService;
    @Autowired
    private SysUdcService sysUdcService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<Long> save(TmsFareTmplSaveVO saveVO) {
        try {
            checkForSave(saveVO, null);
        } catch (Exception e) {
            logger.info("新增运费模板时校验参数不通过：{}", e.getMessage());
            return ApiResult.fail(e.getMessage());
        }

        // 先保存基本信息
        TmsFareTmplDO fareTmplDO = saveVO2DO(saveVO, null);
        tmsFareTmplRepo.save(fareTmplDO);

        // 再保存地区信息
        saveArea(saveVO.getArea(), fareTmplDO.getId());

        return ApiResult.ok(fareTmplDO.getId());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<Long> update(Long id, TmsFareTmplSaveVO saveVO) {
        try {
            checkForSave(saveVO, id);
        } catch (Exception e) {
            logger.info("修改运费模板时校验参数不通过：{}", e.getMessage());
            return ApiResult.fail(e.getMessage());
        }

        // vo转do后保存，先保存基本信息
        TmsFareTmplDO fareTmplDO = saveVO2DO(saveVO, id);
        tmsFareTmplRepo.save(fareTmplDO);

        // 再保存地区信息
        saveArea(saveVO.getArea(), fareTmplDO.getId());

        return ApiResult.ok(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<Long> updateEnable(Long id) {
        if (id == null) {
            return ApiResult.fail("模板ID为空");
        }

        Boolean enabled = tmsFareTmplRepoProc.getEnable(id);
        tmsFareTmplRepoProc.updateEnable(List.of(id), !enabled);

        return ApiResult.ok(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<List<Long>> updateEnable(List<Long> ids, Boolean enabled) {
        if (CollectionUtils.isEmpty(ids)) {
            return ApiResult.fail("模板ID为空");
        }

        enabled = enabled != null && enabled;

        tmsFareTmplRepoProc.updateEnable(ids, enabled);

        return ApiResult.ok(ids);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<Long> delete(Long id) {
        if (id == null) {
            return ApiResult.fail("模板ID为空");
        }

        tmsFareTmplRepoProc.delete(List.of(id));
        return ApiResult.ok(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<List<Long>> delete(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return ApiResult.fail("模板ID为空");
        }

        tmsFareTmplRepoProc.delete(ids);
        return ApiResult.ok(ids);
    }

    @Override
    public ApiResult<TmsFareTmplDetailRespVO> get(Long id) {
        if (id == null) {
            return ApiResult.fail("模板ID为空");
        }

        TmsFareTmplDetailRespVO respVO = tmsFareTmplRepo.findById(id)
                .map(TmsFareTmplConvert.INSTANCE::do2DetailRespVo)
                .orElse(null);
        if (respVO == null) {
            return ApiResult.fail("模板信息不存在");
        }

        // 填充其它信息
        fillRespVo(respVO);

        return ApiResult.ok(respVO);
    }

    @Override
    public ApiResult<String> getName(Long id) {
        if (id == null) {
            return ApiResult.fail("运费模板ID为空");
        }

        String name = tmsFareTmplRepoProc.getName(id);
        return ApiResult.ok(name);
    }

    @Override
    public ApiResult<Boolean> exists(Long id) {
        if (id == null) {
            return ApiResult.fail("运费模板ID为空");
        }

        boolean exists = tmsFareTmplRepo.existsById(id);
        return exists ? ApiResult.ok(true) : ApiResult.fail();
    }

    @Override
    public ApiResult<PagingVO<TmsFareTmplPageRespVO>> search(TmsFareTmplPageParamVO pageParamVO) {
        if (pageParamVO == null) {
            return ApiResult.fail("查询参数为空");
        }

        // 组织条件参数
        QTmsFareTmplDO qTmsFareTmplDO = QTmsFareTmplDO.tmsFareTmplDO;
        Predicate predicate = qTmsFareTmplDO.isNotNull();
        if (StrUtil.isNotBlank(pageParamVO.getKeyword())) {
            String keyword = "%" + pageParamVO.getKeyword() + "%";
            predicate = ExpressionUtils.and(predicate, qTmsFareTmplDO.name.like(keyword));
        }

        predicate = ExpressionUtils.and(predicate, qTmsFareTmplDO.deleteFlag.eq(CommonConstant.COMMON_DELETE_NO));

        // 查询数据
        Page<TmsFareTmplDO> fareTmplDOPage = tmsFareTmplRepo.findAll(predicate, pageParamVO.getPageRequest());
        List<TmsFareTmplPageRespVO> respVOList = do2PageRespVO(fareTmplDOPage.getContent());

        return ApiResult.ok(PagingVO.<TmsFareTmplPageRespVO>builder()
                .total(fareTmplDOPage.getTotalElements())
                .records(respVOList)
                .build());
    }

    @Override
    public ApiResult<List<TmsFareTmplDTO>> query(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return ApiResult.fail("运费模板ID为空");
        }

        TmsFareTmplConvert instance = TmsFareTmplConvert.INSTANCE;
        List<TmsFareTmplDTO> dtoList = tmsFareTmplRepo.findAllById(ids)
                .stream()
                .map(instance::do2DTO)
                .collect(Collectors.toList());
        return ApiResult.ok(dtoList);
    }

    /**
     * 保存前的检查
     *
     * @param saveVO 保存信息，不可为空
     * @param id     模板ID，修改时不可为空
     */
    private void checkForSave(TmsFareTmplSaveVO saveVO, Long id) {
        Assert.notNull(saveVO, "保存信息为空");

        boolean exists = false;
        if (id != null) {
            // 如果修改，则先判断数据是否存在
            exists = tmsFareTmplRepo.existsById(id);
            Assert.isTrue(exists, "修改的数据不存在");
        }

        // 检查模板名称
        exists = tmsFareTmplRepoProc.existsName(saveVO.getName(), id);
        Assert.isFalse(exists, "模板名称已存在");

        Assert.notBlank(saveVO.getCalcMethod(), "请选择费用计算方式");

        saveVO.setWeightStart(ObjectUtil.defaultIfNull(saveVO.getWeightStart(), BigDecimal.ZERO));
        saveVO.setCostStart(ObjectUtil.defaultIfNull(saveVO.getCostStart(), BigDecimal.ZERO));
        saveVO.setWeightIncrease(ObjectUtil.defaultIfNull(saveVO.getWeightIncrease(), BigDecimal.ONE));
        saveVO.setCostIncrease(ObjectUtil.defaultIfNull(saveVO.getCostIncrease(), BigDecimal.ZERO));

        // 检查地区信息
        if (CollectionUtils.isNotEmpty(saveVO.getArea())) {
            List<Long> areaIdList = saveVO.getArea().stream()
                    .flatMap(t -> ListUtil.toList(t.getComAreaProvinceId(), t.getComAreaCityId(),
                            t.getComAreaCountyId(), t.getComAreaStreetId()).stream()
                    ).filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());
            Assert.notEmpty(areaIdList, "选择地区有误");
            exists = comAreaService.existsAll(areaIdList).getData();
            Assert.isTrue(exists, "选择地区不存在");
        }
    }

    /**
     * 基本信息savoVO转DO
     *
     * @param saveVO 保存信息，不可为空
     * @param id     模板ID，修改时不可为空
     * @return DO
     */
    private TmsFareTmplDO saveVO2DO(TmsFareTmplSaveVO saveVO, Long id) {
        TmsFareTmplConvert convert = TmsFareTmplConvert.INSTANCE;
        if (id == null) {
            TmsFareTmplDO fareTmplDO = convert.saveVo2Do(saveVO);
            fareTmplDO.setEnabled(false);

            return fareTmplDO;
        }

        Optional<TmsFareTmplDO> optionalFareTmsDO = tmsFareTmplRepo.findById(id);
        TmsFareTmplDO fareTmplDO = optionalFareTmsDO.orElseThrow();
        convert.saveVo2Do(saveVO, fareTmplDO);

        return fareTmplDO;
    }

    /**
     * 保存模板地区
     *
     * @param areaComVOList 地区列表
     * @param id            模板ID
     */
    private void saveArea(List<TmsFareAreaComVO> areaComVOList, Long id) {
        // 先删除之前的配置
        tmsFareTmplAreaRepoProc.deleteByTmsFareTmplId(id);

        if (CollectionUtils.isNotEmpty(areaComVOList)) {
            TmsFareTmplAreaConvert convert = TmsFareTmplAreaConvert.INSTANCE;
            List<TmsFareTmplAreaDO> areaDOList = areaComVOList.stream()
                    .map(t -> {
                        TmsFareTmplAreaDO areaDO = convert.vo2DO(t);
                        areaDO.setTmsFareTmplId(id);
                        return areaDO;
                    }).collect(Collectors.toList());
            tmsFareTmplAreaRepo.saveAll(areaDOList);
        }
    }

    /**
     * 填充VO的其它信息
     *
     * @param respVO vo信息
     */
    private void fillRespVo(TmsFareTmplDetailRespVO respVO) {
        // 获取计算方式名称
        UdcEnum udcMethod = UdcEnum.TMS_FARE_CALC_METHOD_NUM;
        String calcMethodName = sysUdcService.findOne(udcMethod.getModel(), udcMethod.getCode(), respVO.getCalcMethod())
                .map(SysUdcDO::getValDesc)
                .orElse(null);
        respVO.setCalcMethodName(calcMethodName);

        respVO.setArea(getAreaVO(respVO.getId()));
    }

    /**
     * 获取模板的地区
     *
     * @param id 模板ID
     * @return 模板地区
     */
    private List<TmsFareAreaComVO> getAreaVO(Long id) {
        List<TmsFareTmplAreaDO> areaDOList = tmsFareTmplAreaRepoProc.queryByTmsFareTmplId(id);
        if (CollectionUtils.isEmpty(areaDOList)) {
            return Collections.emptyList();
        }

        // 转vo并设置名称
        List<TmsFareAreaComVO> areaComVOList = TmsFareTmplAreaConvert.INSTANCE.do2VO(areaDOList);
        List<Long> areaIdList = areaComVOList.stream()
                .flatMap(t -> ListUtil.toList(t.getComAreaProvinceId(), t.getComAreaCityId(),
                        t.getComAreaCountyId(), t.getComAreaStreetId()).stream()
                ).filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, String> nameMap = comAreaService.queryName(areaIdList).getData();
        if (MapUtils.isEmpty(nameMap)) {
            return areaComVOList;
        }

        // 设置区域名称
        fillAreaName(nameMap, areaComVOList);

        return areaComVOList;
    }

    /**
     * do转分页list
     *
     * @param fareTmplDOList do list
     * @return vo list
     */
    private List<TmsFareTmplPageRespVO> do2PageRespVO(List<TmsFareTmplDO> fareTmplDOList) {
        if (CollectionUtils.isEmpty(fareTmplDOList)) {
            return Collections.emptyList();
        }

        // 获取计算方式名称
        UdcEnum udcMethod = UdcEnum.TMS_FARE_CALC_METHOD_NUM;
        Map<String, String> calcMethodNameMap = sysUdcService.getCodeMap(udcMethod.getModel(), udcMethod.getCode());

        // 获取区域
        List<Long> idList = fareTmplDOList.stream().map(TmsFareTmplDO::getId).collect(Collectors.toList());
        List<TmsFareTmplAreaDO> areaDOList = tmsFareTmplAreaRepoProc.queryByTmsFareTmplId(idList);
        List<Long> areaIdList = areaDOList.stream()
                .flatMap(t -> ListUtil.toList(t.getComAreaProvinceId(), t.getComAreaCityId(),
                        t.getComAreaCountyId(), t.getComAreaStreetId()).stream()
                ).filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Long, String> nameMap = comAreaService.queryName(areaIdList).getData();
        Map<Long, List<TmsFareTmplAreaDO>> areaMap = areaDOList.stream()
                .collect(Collectors.groupingBy(TmsFareTmplAreaDO::getTmsFareTmplId));

        // 组织返回数据
        TmsFareTmplAreaConvert areaConvert = TmsFareTmplAreaConvert.INSTANCE;
        return TmsFareTmplConvert.INSTANCE.do2PageRespVOList(fareTmplDOList).stream()
                .peek(t -> {
                    t.setCalcMethodName(calcMethodNameMap.get(t.getCalcMethod()));
                    List<TmsFareTmplAreaDO> areaList = areaMap.get(t.getId());
                    if (CollectionUtils.isNotEmpty(areaList)) {
                        List<TmsFareAreaComVO> areaComVOList = areaConvert.do2VO(areaList);
                        // 设置区域名称
                        fillAreaName(nameMap, areaComVOList);

                        t.setArea(areaComVOList);
                    }
                }).collect(Collectors.toList());
    }

    /**
     * 设置区域名称
     *
     * @param nameMap       id与名称映射
     * @param areaComVOList 区域
     */
    private void fillAreaName(Map<Long, String> nameMap, List<TmsFareAreaComVO> areaComVOList) {
        areaComVOList.forEach(t -> {
            t.setComAreaProvinceName(nameMap.get(t.getComAreaProvinceId()));
            t.setComAreaCityName(nameMap.get(t.getComAreaCityId()));
            t.setComAreaCountyName(nameMap.get(t.getComAreaCountyId()));
            t.setComAreaStreetName(nameMap.get(t.getComAreaStreetId()));

            List<String> names = ListUtil.toList(t.getComAreaProvinceName(), t.getComAreaCityName(), t.getComAreaCountyName(),
                    t.getComAreaStreetName()).stream()
                    .filter(StrUtil::isNotBlank)
                    .collect(Collectors.toList());
            t.setAreaNames(names);
        });
    }
}